#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

enum
{
    SocketError = 1,
    BindError,
    ListenError,

};

namespace NetWork
{
    // 封装套接字的接口类   作为基类
    // 设计模式：模版方法类
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void CreateSocket() = 0;
        virtual void BindSocket(uint16_t port) = 0;
        virtual void ListenSocket(int backlog) = 0;
        virtual Socket *AcceptConnection(std::string *peer_ip, uint16_t *peer_port) = 0;
        virtual bool ConnectServer(std::string &server_ip, uint16_t &server_port) = 0;
        virtual int GetSockFd() = 0;
        virtual void CloseSockFd() = 0;
        virtual void SetSockFd(int sockfd) = 0;
        virtual bool Recv(std::string *buffer, int size) = 0;
        virtual bool Send(std::string &send_package) = 0;

    public:
        // 服务器创建监听套接字
        void BuildListenSocketMethod(uint16_t port, int backlog = 5)
        {
            CreateSocket();
            BindSocket(port);
            ListenSocket(backlog);
        }
        // 客户端创建链接套接字
        bool BuildConnectSocketMethod(std::string &server_ip, uint16_t server_port)
        {
            CreateSocket();
            return ConnectServer(server_ip, server_port);
        }
        void BuildNormalSocketMethod(int sockfd)
        {
            SetSockFd(sockfd);
        }
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = -1) : _sock_fd(sockfd) {}
        ~TcpSocket() {}

        void CreateSocket() override
        {
            _sock_fd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sock_fd < 0)
            {
                exit(SocketError);
            }
        }
        void BindSocket(uint16_t port) override
        {
            struct sockaddr_in local;
            bzero(&local, sizeof(local));

            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(port); // 本地转网络

            int n = ::bind(_sock_fd, (struct sockaddr *)&local, sizeof(local));
            if (n < 0)
            {
                exit(BindError);
            }
        }
        void ListenSocket(int backlog) override
        {
            int n = listen(_sock_fd, backlog);
            if (n < 0)
            {
                exit(ListenError);
            }
        }
        Socket *AcceptConnection(std::string *peer_ip, uint16_t *peer_port) override
        {
            struct sockaddr_in peer;
            socklen_t length = sizeof(peer);
            int new_sock_fd = ::accept(_sock_fd, (struct sockaddr *)&peer, &length);
            if (new_sock_fd < 0)
            {
                return nullptr;
            }

            // 输出型参数服务器获取accept成功的客户端信息
            *peer_ip = inet_ntoa(peer.sin_addr);
            *peer_port = ntohs(peer.sin_port);
            Socket *ret = new TcpSocket(new_sock_fd);
            return ret;
        }
        bool ConnectServer(std::string &server_ip, uint16_t &server_port) override
        {
            struct sockaddr_in server;
            bzero(&server, sizeof(server));

            server.sin_family = AF_INET;
            server.sin_addr.s_addr = inet_addr(server_ip.c_str());
            server.sin_port = htons(server_port);
            int n = ::connect(_sock_fd, (struct sockaddr *)&server, sizeof(server));

            if (n == 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

    public:
        int GetSockFd() override
        {
            return _sock_fd;
        }
        void CloseSockFd() override
        {
            if (_sock_fd > -1)
            {
                ::close(_sock_fd);
            }
        }
        void SetSockFd(int sockfd) override
        {
            _sock_fd = sockfd;
        }
        bool Recv(std::string *in, int size = 1024) override
        {
            char buffer[size];
            ssize_t n = recv(_sock_fd, buffer, size - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                *in += buffer;
                return true;
            }
            std::cout << "recv failed" << std::endl;
            return false;
        }
        bool Send(std::string &send_package) override
        {
            ssize_t n = send(_sock_fd, send_package.c_str(), send_package.size(), 0);
            if (n > 0)
            {
                return true;
            }
            return false;
        }

    private:
        int _sock_fd;
    };
}